package com.example.demo.annotation;

import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Auther Created by l.wang on 2019/1/15.
 */
class ExpressionEvaluator extends CachedExpressionEvaluator {
    private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();

    private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<>(128);

    private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(128);


    /**
     * 获取方法中所有参数
     * @param method 方法
     * @param args 方法参数
     * @param target
     * @param targetClass 添加注解目标类
     * @return
     */
    public EvaluationContext createEvaluationContext(Method method, Object[] args, Object target, Class<?> targetClass) {
        LockExpressionRootObject rootObject = new LockExpressionRootObject(method, args, target, targetClass);//构造函数并效验类对象与方法不为空
        Method targetMethod = getTargetMethod(targetClass, method); //效验是否有此类对象与方法在执行
        return new MethodBasedEvaluationContext(rootObject,
                targetMethod, args, this.paramNameDiscoverer);
    }

    /**
     * 获取注解定义的参数与方法相同的参数值
     * @param keyExpression 注解中的参数
     * @param methodKey
     * @param evalContext 上下文
     * @return
     */
    public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
        return getExpression(this.keyCache, methodKey, keyExpression).getValue(evalContext);
    }

    /**
     * Clear all caches.
     */
    void clear() {
        this.keyCache.clear();
        this.targetMethodCache.clear();
    }

    /**
     * 效验类对象与方法
     * @param targetClass
     * @param method
     * @return
     */
    private Method getTargetMethod(Class<?> targetClass, Method method) {
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
        Method targetMethod = this.targetMethodCache.get(methodKey);
        if (targetMethod == null) {
            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            if (targetMethod == null) {
                targetMethod = method;
            }
            this.targetMethodCache.put(methodKey, targetMethod);
        }
        return targetMethod;
    }
}

